﻿using FreeImageAPI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace topng
{
    public enum ElemFormat
    {
        BYTE,
        BYTE_DoubleBuf,
        FLOAT16,
        FLOAT32,
    }
    class ExportBuffer
    {
        float[] fbuffer;
        float[] fbuffer_b;
        int width;
        int height;
        static float ReadFormPtr(IntPtr ptr)
        {
            var sr = (uint)Marshal.ReadInt32(ptr);
            byte[] bs = BitConverter.GetBytes(sr);
            float r = BitConverter.ToSingle(bs, 0);

            return r;
        }

        public delegate void ConvertColor(ref float r, ref float g, ref float b, ref float a);
        public void ParseBitmap(FreeImageBitmap source, ConvertColor parser, ConvertColor parser_b = null)
        {
            this.width = source.Width;
            this.height = source.Height;
            fbuffer = new float[source.Width * source.Height * 4];
            if (parser_b != null)
            {
                fbuffer_b = new float[source.Width * source.Height * 4];
            }

            IntPtr intPtr = source.Bits;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    float r = ReadFormPtr(intPtr);
                    intPtr += 4;
                    float g = ReadFormPtr(intPtr);
                    intPtr += 4;
                    float b = ReadFormPtr(intPtr);
                    intPtr += 4;

                    float a = 1;
                    if (source.ImageType == FreeImageAPI.FREE_IMAGE_TYPE.FIT_RGBF)
                    {

                    }
                    else
                    {
                        a = ReadFormPtr(intPtr);
                        intPtr += 4;
                    }
                    int seek = (i * width + j) * 4;
                    {
                        float r2 = r, g2 = g, b2 = b, a2 = a;
                        parser(ref r2, ref g2, ref b2, ref a2);
                        fbuffer[seek] = r2;
                        fbuffer[seek + 1] = g2;
                        fbuffer[seek + 2] = b2;
                        fbuffer[seek + 3] = a2;
                    }
                    if (parser_b != null)
                    {
                        float r2 = r, g2 = g, b2 = b, a2 = a;
                        parser_b(ref r2, ref g2, ref b2, ref a2);
                        fbuffer_b[seek] = r2;
                        fbuffer_b[seek + 1] = g2;
                        fbuffer_b[seek + 2] = b2;
                        fbuffer_b[seek + 3] = a2;
                    }
                }
            }
        }
        public void SaveAsPng(string filename, string filename_b = null)
        {
            Bitmap bitmap = new Bitmap(width, height);
            Bitmap bitmap_b = null;

            if (filename_b != null)
                bitmap_b = new Bitmap(width, height);

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int colorseek = ((height - 1 - i) * width + j) * 4;
                    //int seet = i* width + j) * 4;
                    {
                        int ir = (int)(fbuffer[colorseek] * 255);
                        int ig = (int)(fbuffer[colorseek + 1] * 255);
                        int ib = (int)(fbuffer[colorseek + 2] * 255);
                        int ia = (int)(fbuffer[colorseek + 3] * 255);
                        if (ir < 0) ir = 0;
                        if (ir > 255) ir = 255;
                        if (ig < 0) ig = 0;
                        if (ig > 255) ig = 255;
                        if (ib < 0) ib = 0;
                        if (ib > 255) ib = 255;
                        if (ia < 0) ia = 0;
                        if (ia > 255) ia = 255;
                        bitmap.SetPixel(j, i, Color.FromArgb(ia, ir, ig, ib));
                    }
                    if (bitmap_b != null)
                    {
                        int ir = (int)(fbuffer_b[colorseek] * 255);
                        int ig = (int)(fbuffer_b[colorseek + 1] * 255);
                        int ib = (int)(fbuffer_b[colorseek + 2] * 255);
                        int ia = (int)(fbuffer_b[colorseek + 3] * 255);
                        if (ir < 0) ir = 0;
                        if (ir > 255) ir = 255;
                        if (ig < 0) ig = 0;
                        if (ig > 255) ig = 255;
                        if (ib < 0) ib = 0;
                        if (ib > 255) ib = 255;
                        if (ia < 0) ia = 0;
                        if (ia > 255) ia = 255;
                        bitmap_b.SetPixel(j, i, Color.FromArgb(ia, ir, ig, ib));
                    }
                }
            }
            bitmap.Save(filename);
            if (bitmap_b != null)
                bitmap_b.Save(filename_b);
        }
        public void SaveAsRaw16(string filename)
        {
            var length = 2 + 2 + (width * height * 4 * 2);
            var data = new byte[length];
            var bw = BitConverter.GetBytes(width);
            var bh = BitConverter.GetBytes(height);
            data[0] = bw[0];
            data[1] = bw[1];
            data[2] = bh[0];
            data[3] = bh[1];

            var hr = new HalfFloat();
            var hg = new HalfFloat();
            var hb = new HalfFloat();
            var ha = new HalfFloat();
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int colorseek = (i * width + j) * 4;

                    int seek = 4 + ((height - i - 1) * width + j) * 8;
                    {
                        hr.value = (float)fbuffer[colorseek];
                        hg.value = (float)fbuffer[colorseek + 1];
                        hb.value = (float)fbuffer[colorseek + 2];
                        ha.value = (float)fbuffer[colorseek + 3];
                        data[seek + 0] = hr.low;
                        data[seek + 1] = hr.high;

                        data[seek + 2] = hg.low;
                        data[seek + 3] = hg.high;

                        data[seek + 4] = hb.low;
                        data[seek + 5] = hb.high;

                        data[seek + 6] = ha.low;
                        data[seek + 7] = ha.high;
                    }
                }
            }
            System.IO.File.WriteAllBytes(filename, data);
        }
        public void SaveAsRaw32(string filename)
        {
            var length = 2 + 2 + (width * height * 4 * 4);
            var data = new byte[length];
            var bw = BitConverter.GetBytes(width);
            var bh = BitConverter.GetBytes(height);
            data[0] = bw[0];
            data[1] = bw[1];
            data[2] = bh[0];
            data[3] = bh[1];

            var fr = new FloatUint();
            var fg = new FloatUint();
            var fb = new FloatUint();
            var fa = new FloatUint();
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int colorseek = (i * width + j) * 4;
                    int seek = 4 + ((height - i - 1) * width + j) * 16;
                    {
                        fr._float = (float)fbuffer[colorseek];
                        fg._float = (float)fbuffer[colorseek + 1];
                        fb._float = (float)fbuffer[colorseek + 2];
                        fa._float = (float)fbuffer[colorseek + 3];


                        data[seek + 0] = fr.b0;
                        data[seek + 1] = fr.b1;
                        data[seek + 2] = fr.b2;
                        data[seek + 3] = fr.b3;

                        data[seek + 4] = fg.b0;
                        data[seek + 5] = fg.b1;
                        data[seek + 6] = fg.b2;
                        data[seek + 7] = fg.b3;

                        data[seek + 8] = fb.b0;
                        data[seek + 9] = fb.b1;
                        data[seek + 10] = fb.b2;
                        data[seek + 11] = fb.b3;

                        data[seek + 12] = fa.b0;
                        data[seek + 13] = fa.b1;
                        data[seek + 14] = fa.b2;
                        data[seek + 15] = fa.b3;
                    }
                }
            }
            System.IO.File.WriteAllBytes(filename, data);
        }
    }
}
